package org.example.mybatis.session;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.example.mybatis.annotation.Delete;
import org.example.mybatis.annotation.Insert;
import org.example.mybatis.annotation.Select;
import org.example.mybatis.annotation.Update;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 配置类，负责解析配置文件并维护全局配置信息
 */
public class Configuration {
    private static final Logger logger = LoggerFactory.getLogger(Configuration.class);

    // 数据库连接相关信息
    private String driver;
    private String url;
    private String username;
    private String password;

    // 存储映射文件信息，key为完全限定名(namespace.id)，value为SQL语句
    private final Map<String, MappedStatement> mappedStatements = new HashMap<>();

    /**
     * 解析配置文件
     */
    public void parse(String resource) {
        InputStream inputStream = getResourceAsStream(resource);
        Document document;
        try {
            SAXReader reader = new SAXReader();
            document = reader.read(inputStream);
            Element root = document.getRootElement();

            // 解析数据源配置
            parseDataSource(root);

            // 解析映射器配置
            parseMappers(root);
        } catch (DocumentException e) {
            logger.error("解析配置文件出错", e);
            throw new RuntimeException("解析配置文件出错", e);
        }
    }

    /**
     * 解析数据源配置
     */
    private void parseDataSource(Element root) {
        Element dataSource = root.element("dataSource");
        List<Element> properties = dataSource.elements("property");

        for (Element property : properties) {
            String name = property.attributeValue("name");
            String value = property.attributeValue("value");

            if ("driver".equals(name)) {
                this.driver = value;
            } else if ("url".equals(name)) {
                this.url = value;
            } else if ("username".equals(name)) {
                this.username = value;
            } else if ("password".equals(name)) {
                this.password = value;
            }
        }
    }

    /**
     * 解析映射器配置
     */
    private void parseMappers(Element root) {
        Element mappers = root.element("mappers");
        List<Element> mapperList = mappers.elements("mapper");

        for (Element mapper : mapperList) {
            // 处理XML配置方式
            if (mapper.attributeValue("resource") != null) {
                String resource = mapper.attributeValue("resource");
                parseMapper(resource);
            }
            // 处理注解方式
            else if (mapper.attributeValue("class") != null) {
                String className = mapper.attributeValue("class");
                parseMapperAnnotations(className);
            }
        }
    }

    /**
     * 解析Mapper类的注解
     */
    private void parseMapperAnnotations(String className) {
        try {
            Class<?> mapperInterface = Class.forName(className);
            String namespace = mapperInterface.getName();

            // 遍历接口中的所有方法
            Method[] methods = mapperInterface.getMethods();
            for (Method method : methods) {
                // 处理@Select注解
                if (method.isAnnotationPresent(Select.class)) {
                    Select selectAnnotation = method.getAnnotation(Select.class);
                    String sql = selectAnnotation.value();
                    String id = method.getName();

                    // 获取真正的返回类型
                    String resultType = getReturnType(method);

                    MappedStatement mappedStatement = new MappedStatement();
                    mappedStatement.setId(id);
                    mappedStatement.setNamespace(namespace);
                    mappedStatement.setResultType(resultType);
                    mappedStatement.setSql(sql);
                    mappedStatement.setSqlType("select");

                    String key = namespace + "." + id;
                    mappedStatements.put(key, mappedStatement);
                    logger.info("已加载注解SQL: {}", key);
                }
                // 处理@Insert注解
                else if (method.isAnnotationPresent(Insert.class)) {
                    Insert insertAnnotation = method.getAnnotation(Insert.class);
                    String sql = insertAnnotation.value();
                    String id = method.getName();

                    MappedStatement mappedStatement = new MappedStatement();
                    mappedStatement.setId(id);
                    mappedStatement.setNamespace(namespace);
                    mappedStatement.setSql(sql);
                    mappedStatement.setSqlType("insert");

                    String key = namespace + "." + id;
                    mappedStatements.put(key, mappedStatement);
                    logger.info("已加载注解SQL: {}", key);
                }
                // 处理@Update注解
                else if (method.isAnnotationPresent(Update.class)) {
                    Update updateAnnotation = method.getAnnotation(Update.class);
                    String sql = updateAnnotation.value();
                    String id = method.getName();

                    MappedStatement mappedStatement = new MappedStatement();
                    mappedStatement.setId(id);
                    mappedStatement.setNamespace(namespace);
                    mappedStatement.setSql(sql);
                    mappedStatement.setSqlType("update");

                    String key = namespace + "." + id;
                    mappedStatements.put(key, mappedStatement);
                    logger.info("已加载注解SQL: {}", key);
                }
                // 处理@Delete注解
                else if (method.isAnnotationPresent(Delete.class)) {
                    Delete deleteAnnotation = method.getAnnotation(Delete.class);
                    String sql = deleteAnnotation.value();
                    String id = method.getName();

                    MappedStatement mappedStatement = new MappedStatement();
                    mappedStatement.setId(id);
                    mappedStatement.setNamespace(namespace);
                    mappedStatement.setSql(sql);
                    mappedStatement.setSqlType("delete");

                    String key = namespace + "." + id;
                    mappedStatements.put(key, mappedStatement);
                    logger.info("已加载注解SQL: {}", key);
                }
            }
        } catch (ClassNotFoundException e) {
            logger.error("找不到Mapper接口: {}", className, e);
            throw new RuntimeException("找不到Mapper接口: " + className, e);
        }
    }

    /**
     * 获取方法的实际返回类型
     * 如果返回集合，则获取集合的泛型类型
     */
    private String getReturnType(Method method) {
        Class<?> returnType = method.getReturnType();

        // 如果返回类型是集合类型，获取其泛型参数
        if (Collection.class.isAssignableFrom(returnType)) {
            Type genericReturnType = method.getGenericReturnType();
            if (genericReturnType instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) genericReturnType;
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                if (actualTypeArguments != null && actualTypeArguments.length > 0) {
                    Type actualType = actualTypeArguments[0];
                    if (actualType instanceof Class) {
                        logger.info("集合类型的泛型参数为: {}", ((Class<?>) actualType).getName());
                        return ((Class<?>) actualType).getName();
                    }
                }
            }
            // 如果无法确定泛型类型，尝试从方法名判断
            String methodName = method.getName();
            if (methodName.startsWith("select") || methodName.startsWith("find") || methodName.startsWith("get")) {
                // 方法名中可能包含实体类信息，例如findUserById，尝试提取User
                // 这里使用一个简化的判断
                for (String entityName : new String[]{"User", "Product", "Order", "Item"}) {
                    if (methodName.contains(entityName)) {
                        try {
                            String guessedClassName = "org.example.mybatis.entity." + entityName;
                            Class.forName(guessedClassName);
                            logger.info("从方法名猜测集合元素类型为: {}", guessedClassName);
                            return guessedClassName;
                        } catch (ClassNotFoundException ignored) {
                            // 忽略错误，继续尝试
                        }
                    }
                }
            }
            logger.warn("无法确定集合返回类型的泛型类型，方法: {}，将使用User类型", method.getName());
            return "org.example.mybatis.entity.User"; // 默认假设是User类型，便于测试
        }

        // 非集合类型，直接返回
        return returnType.getName();
    }

    /**
     * 解析具体映射文件
     */
    private void parseMapper(String resource) {
        InputStream inputStream = getResourceAsStream(resource);
        Document document;
        try {
            SAXReader reader = new SAXReader();
            document = reader.read(inputStream);
            Element root = document.getRootElement();

            // 获取命名空间
            String namespace = root.attributeValue("namespace");

            // 解析查询语句
            List<Element> selectElements = root.elements("select");
            for (Element selectElement : selectElements) {
                String id = selectElement.attributeValue("id");
                String resultType = selectElement.attributeValue("resultType");
                String sql = selectElement.getTextTrim();

                MappedStatement mappedStatement = new MappedStatement();
                mappedStatement.setId(id);
                mappedStatement.setNamespace(namespace);
                mappedStatement.setResultType(resultType);
                mappedStatement.setSql(sql);
                mappedStatement.setSqlType("select");

                String key = namespace + "." + id;
                mappedStatements.put(key, mappedStatement);
                logger.info("已加载SQL: {}", key);
            }

            // 解析插入语句
            List<Element> insertElements = root.elements("insert");
            for (Element insertElement : insertElements) {
                String id = insertElement.attributeValue("id");
                String parameterType = insertElement.attributeValue("parameterType");
                String sql = insertElement.getTextTrim();

                MappedStatement mappedStatement = new MappedStatement();
                mappedStatement.setId(id);
                mappedStatement.setNamespace(namespace);
                mappedStatement.setParameterType(parameterType);
                mappedStatement.setSql(sql);
                mappedStatement.setSqlType("insert");

                String key = namespace + "." + id;
                mappedStatements.put(key, mappedStatement);
                logger.info("已加载SQL: {}", key);
            }

            // 解析更新语句
            List<Element> updateElements = root.elements("update");
            for (Element updateElement : updateElements) {
                String id = updateElement.attributeValue("id");
                String parameterType = updateElement.attributeValue("parameterType");
                String sql = updateElement.getTextTrim();

                MappedStatement mappedStatement = new MappedStatement();
                mappedStatement.setId(id);
                mappedStatement.setNamespace(namespace);
                mappedStatement.setParameterType(parameterType);
                mappedStatement.setSql(sql);
                mappedStatement.setSqlType("update");

                String key = namespace + "." + id;
                mappedStatements.put(key, mappedStatement);
                logger.info("已加载SQL: {}", key);
            }

            // 解析删除语句
            List<Element> deleteElements = root.elements("delete");
            for (Element deleteElement : deleteElements) {
                String id = deleteElement.attributeValue("id");
                String parameterType = deleteElement.attributeValue("parameterType");
                String sql = deleteElement.getTextTrim();

                MappedStatement mappedStatement = new MappedStatement();
                mappedStatement.setId(id);
                mappedStatement.setNamespace(namespace);
                mappedStatement.setParameterType(parameterType);
                mappedStatement.setSql(sql);
                mappedStatement.setSqlType("delete");

                String key = namespace + "." + id;
                mappedStatements.put(key, mappedStatement);
                logger.info("已加载SQL: {}", key);
            }

        } catch (DocumentException e) {
            logger.error("解析映射文件出错", e);
            throw new RuntimeException("解析映射文件出错", e);
        }
    }

    /**
     * 获取资源输入流
     */
    private InputStream getResourceAsStream(String resource) {
        return this.getClass().getClassLoader().getResourceAsStream(resource);
    }

    /**
     * 创建数据库连接
     */
    public Connection getConnection() {
        try {
            Class.forName(driver);
            return DriverManager.getConnection(url, username, password);
        } catch (ClassNotFoundException | SQLException e) {
            logger.error("获取数据库连接失败", e);
            throw new RuntimeException("获取数据库连接失败", e);
        }
    }

    /**
     * 根据statementId获取MappedStatement
     */
    public MappedStatement getMappedStatement(String statementId) {
        return mappedStatements.get(statementId);
    }
}